package com.xyh.transaction.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.payment.common.models.AlipayTradeQueryResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.xyh.transaction.common.Constant;
import com.xyh.transaction.common.MessageType;
import com.xyh.transaction.common.OrderStatus;
import com.xyh.transaction.config.AlipayConfig;
import com.xyh.transaction.entity.*;
import com.xyh.transaction.entity.dto.AlipayNotifyParam;
import com.xyh.transaction.entity.vo.GoodsVo;
import com.xyh.transaction.entity.vo.PayInfo;
import com.xyh.transaction.exception.AlipayApiException;
import com.xyh.transaction.exception.BusinessException;
import com.xyh.transaction.listener.MapGoodsImageEvent;
import com.xyh.transaction.mapper.*;
import com.xyh.transaction.queue.OrderDelayQueue;
import com.xyh.transaction.response.ResponseResult;
import com.xyh.transaction.service.OrderService;
import com.xyh.transaction.utils.EmailUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Api(tags = "支付接口管理")
@Slf4j
@RestController
@RequestMapping("pay")
public class PayController {

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    @Autowired
    private OrderService orderService;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private MessageMapper messageMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private AlipayConfig alipayConfig;
    @Autowired
    private OrderDelayQueue orderDelayQueue;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Autowired
    private EmailUtil emailUtil;

    @Value("${email.enabled}")
    private boolean emailEnabled;

    // 将request中的参数转换成Map
    private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        Map<String, String> retMap = new HashMap<>();

        Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();

        for (Map.Entry<String, String[]> entry : entrySet) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            log.info("key--{},value--->{}", name, Arrays.toString(values));
            int valLen = values.length;

            if (valLen == 1) {
                retMap.put(name, values[0]);
            } else if (valLen > 1) {
                StringBuilder sb = new StringBuilder();
                for (String val : values) {
                    sb.append(",").append(val);
                }
                retMap.put(name, sb.substring(1));
            } else {
                retMap.put(name, "");
            }
        }

        return retMap;
    }

    @ApiOperation(value = "支付宝接口", notes = "支付宝沙箱支付环境")
    @GetMapping("alipay")
    public String pay(AliPay aliPay) {
        AlipayTradePagePayResponse response;
        try {
            Order order = orderMapper.findById(aliPay.getOrderId());
            //  发起API调用（以创建当面付收款二维码为例）
            // TODO 中文乱码问题 easy版sdk无法解决，需要换到通用版sdk
            response = Factory.Payment.Page()
                    .pay(URLEncoder.encode("YW-Goods", "UTF-8"), order.getOrderNo(), String.valueOf(order.getAmount()), aliPay.getReturnUrl());
        } catch (Exception e) {
            log.error("调用遭遇异常，原因：{}", e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }
        return response.getBody();
    }

    @ApiOperation(value = "支付信息", notes = "返回订单的支付信息")
    @GetMapping("/{id}")
    public ResponseResult<PayInfo> getPayInfo(@PathVariable Integer id) {
        PayInfo payInfo = orderService.getPayInfo(id);
        return ResponseResult.success("查询成功！", payInfo);
    }

    @ApiOperation(value = "支付结果异步回调", notes = "返回支付结果(本地无法访问，需要线上环境才可以访问)")
    @PostMapping("callback")
    public String callback(HttpServletRequest request) {
        Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中
        String paramsJson = JSON.toJSONString(params);
        log.info("支付宝回调，{}", paramsJson);
        try {
            // 调用SDK验证签名
            Boolean signVerified = Factory.Payment.Common().verifyNotify(params);
            if (signVerified) {
                log.info("支付宝回调签名认证成功");
                // 按照支付结果异步通知中的描述，对支付结果中的业务内容进行1\2\3\4二次校验，校验成功后在response中返回success，校验失败返回failure
                this.check(params);
                // 另起线程处理业务
                executorService.execute(() -> {
                    AlipayNotifyParam param = buildAlipayNotifyParam(params);
                    String trade_status = param.getTradeStatus();
                    // 支付成功
                    if ("TRADE_SUCCESS".equals(trade_status)
                            || "TRADE_FINISHED".equals(trade_status)) {
                        // 处理支付成功逻辑
                        try {
                            String orderNo = param.getOutTradeNo();
                            Order order = orderMapper.findByOrderNo(orderNo);
                            processPayResult(order, String.valueOf(param.getTotalAmount()));
                            log.info("处理业务代码！");

                        } catch (Exception e) {
                            log.error("支付宝回调业务处理报错,params:" + paramsJson, e);
                        }
                    } else {
                        log.error("没有处理支付宝回调业务，支付宝交易状态：{},params:{}", trade_status, paramsJson);
                    }
                });
                // 如果签名验证正确，立即返回success，后续业务另起线程单独处理
                // 业务处理失败，可查看日志进行补偿，跟支付宝已经没多大关系。
                return "success";
            } else {
                log.info("支付宝回调签名认证失败，signVerified=false, paramsJson:{}", paramsJson);
                return "failure";
            }
        } catch (Exception e) {
            log.error("支付宝回调签名认证失败,paramsJson:{},errorMsg:{}", paramsJson, e.getMessage(), e);
            return "failure";
        }
    }

    @ApiOperation(value = "查询支付结果", notes = "返回orderId订单的付款结果")
    @GetMapping("/payStatus/{orderId}")
    public ResponseResult<PayStatus> payStatus(@PathVariable Integer orderId) {
        AlipayTradeQueryResponse query = null;
        Order order = orderMapper.findById(orderId);
        try {
            query = Factory.Payment.Common().query(order.getOrderNo());
        } catch (Exception e) {
            log.error("查询订单 {} 支付结果失败", order.getOrderNo(), e);
            throw new BusinessException("查询支付结果异常", e);
        }
        String httpBody = query.getHttpBody();
        JSONObject jsonObject = JSONObject.parseObject(httpBody);
        JSONObject query_response = jsonObject.getJSONObject("alipay_trade_query_response");
        String trade_status = query_response.getString("trade_status");
        String payMoney = query_response.getString("total_amount");
        PayStatus payStatus = new PayStatus();
        payStatus.setPayMoney(new BigDecimal(payMoney));
        if (StringUtils.isNotBlank(trade_status)) {
            if (!order.getStatus().equals(OrderStatus.WAIT_PAY)) {
                payStatus.setPayResult(trade_status);
                return ResponseResult.success("查询成功！", payStatus);
            }
            processPayResult(order, payMoney);
        } else {
            trade_status = "NO_SCAN_CODE";
        }

        payStatus.setPayResult(trade_status);
        return ResponseResult.success("查询成功！", payStatus);
    }

    @ApiOperation(value = "下载对账单", notes = "返回对账单的下载地址")
    @GetMapping("/download-bill")
    public String downloadBill(@RequestParam String billDate, @RequestParam String billType) {
        try {
            String httpBody = Factory.Payment.Common().downloadBill(billType, billDate).getHttpBody();

            JSONObject jsonObject = JSONObject.parseObject(httpBody);
            JSONObject queryResponse = jsonObject
                    .getJSONObject("alipay_data_dataservice_bill_downloadurl_query_response");

            if (queryResponse == null || !Objects.equals(queryResponse.getString("code"), "10000")){
                log.error("下载对账单失败：{}", httpBody);
                return "下载失败：账单不存在";
            }

            return queryResponse.getString("bill_download_url");
        } catch (Exception e) {
            log.error("下载对账单失败：{}", e.getMessage(), e);
            return "下载失败：" + e.getMessage();
        }
    }

    @ApiOperation(value = "退款", notes = "返回退款结果")
    @PostMapping("/refund")
    public String refund(@RequestParam String orderId, @RequestParam String refundAmount) {
        try {
            Order order = orderMapper.findById(Integer.valueOf(orderId));
            if (order == null) {
                throw new AlipayApiException("订单不存在");
            }

            String orderNo = order.getOrderNo();

            String httpBody = Factory.Payment.Common().refund(orderNo, refundAmount).getHttpBody();

            JSONObject jsonObject = JSONObject.parseObject(httpBody);
            JSONObject queryResponse = jsonObject
                    .getJSONObject("alipay_trade_refund_response");

            if(queryResponse == null){
                log.error("退款失败：{}", httpBody);
                return "退款失败：支付宝系统异常";
            }

            if (!Objects.equals(queryResponse.getString("code"), "10000")){
                log.error("退款失败：{}", httpBody);
                return "退款失败：" + queryResponse.getString("sub_msg");
            }

            return "订单【"+ orderNo +"】 退款成功！";
        } catch (Exception e) {
            return "退款失败：" + e.getMessage();
        }
    }



    private void processPayResult(Order order, String payMoney) {
        order.setStatus(OrderStatus.WAIT_DELIVERY);
        order.setPayTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());
        orderMapper.updateById(order);

        orderDelayQueue.removeProcessedOrder(order.getId() + "_" + order.getOrderNo());

        // 添加消息，提醒卖家发货
        // 买家信息
        User buyer = userMapper.findById(order.getCreateBy());
        Goods goods = goodsMapper.findById(orderDetailMapper.findByOrderId(order.getId()).get(0).getGoodsId());

        Message message = new Message();
        message.setMessageType(MessageType.REMIND);
        message.setUserId(goods.getCreateBy());// 卖家
        message.setTitle("订单：【" + order.getOrderNo() + "】付款成功，请尽快发货！");
        message.setRead(0);// 未读
        message.setContent("订单号：【" + order.getOrderNo() + "】");
        message.setCreateTime(LocalDateTime.now());
        message.setCreateBy(buyer.getId());
        message.setUpdateTime(LocalDateTime.now());
        message.setUpdateBy(buyer.getId());
        messageMapper.insert(message);

        // 发送邮件通知卖家、买家
        if (!emailEnabled) {
            log.info("邮件通知功能未开启");
            return;
        }

        // 发送发票给买家
        emailUtil.sendInvoiceToBuyer(String.valueOf(order.getId()), "订单付款通知");

        // 卖家信息
        User seller = userMapper.findById(goods.getCreateBy());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Constant.DATE_FORMAT_DEFAULT);
        GoodsVo goodsVo = new GoodsVo();
        eventPublisher.publishEvent(new MapGoodsImageEvent(goods, goodsVo));
        String mailContent = "您于 " + goods.getCreateTime().format(formatter) + " 发布的商品【" + goods.getName() + "】" +
                "已被买家：" + buyer.getUsername() + " 成功下单并完成支付。请尽快安排发货，祝生意兴隆！";
        emailUtil.sendGoodsMsgToSeller(
                seller.getEmail(),
                "商品售出通知",
                mailContent,
                goodsVo.getPicture(),
                goods.getName(),
                order.getOrderNo(),
                payMoney,
                order.getPayTime().format(formatter));
    }

    private AlipayNotifyParam buildAlipayNotifyParam(Map<String, String> params) {
        String json = JSON.toJSONString(params);
        return JSON.parseObject(json, AlipayNotifyParam.class);
    }

    // 验签
    private void check(Map<String, String> params) {
        String outTradeNo = params.get("out_trade_no");

        // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
        Order order = orderMapper.findByOrderNo(outTradeNo); // 这个方法自己实现
        if (order == null) {
            throw new AlipayApiException("out_trade_no错误");
        }

        // 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
        BigDecimal total_amount = new BigDecimal(params.get("total_amount"));
        if (!total_amount.equals(order.getAmount())) {
            throw new AlipayApiException("error total_amount");
        }

        // 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
        // 第三步可根据实际情况省略

        // 4、验证app_id是否为该商户本身。
        if (!params.get("app_id").equals(alipayConfig.getAppId())) {
            throw new AlipayApiException("app_id不一致");
        }
    }
}
